import { BufferAttribute, BufferGeometry, DoubleSide, Mesh, ShaderMaterial, ShaderMaterialParameters, Sphere, Vector2, Vector3 } from 'three'

export interface FixAxisSpriteMaterialOption extends ShaderMaterialParameters {
  center: Vector2
  axis: 'x' | 'y' | 'z' | 'xyz' | ''
  direction: Vector3
  size: Vector2
  scale: Vector2
}

export class FixAxisSpriteMaterial extends ShaderMaterial {
  constructor(option: FixAxisSpriteMaterialOption) {
    super(analyseOption(option))
  }
}

function analyseOption(option: any) {
  const res: any = {
    transparent: true,
    depthTest: false,
    depthWrite: false,
    uniforms: {},
    side: DoubleSide,
    fragmentShader: `
    varying vec2 v_uv;
    void main() { 
      gl_FragColor = vec4(v_uv,0.0,1.0);
    }
    `
  }
  for (const key in option) {
    switch (key) {
      case 'scale':
      case 'size':
      case 'center':
      case 'direction':
        res.uniforms![`u_${key}`] = {
          value: option[key]
        }
        break
      case 'uniforms':
        Object.assign(res.uniforms!, option[key])
        break
      case 'axis':
        res.vertexShader = glsl[option[key]] || glsl.none
        break
      default:
        res[key] = option[key]
        break
    }
  }
  return res
}

const geometry = new BufferGeometry()
geometry.setAttribute('uv', new BufferAttribute(new Float32Array([
  0, 1,
  0, 0,
  1, 1,
  1, 0
]), 2))
geometry.index = new BufferAttribute(new Uint16Array([0, 1, 2, 2, 1, 3]), 1)

geometry.boundingSphere = new Sphere(new Vector3(), 1)
// export function createFixAxisSpriteMesh (material: FixAxisSpriteMaterial) {
//   return new Mesh(geometry, material)
// }

export class FixAxisSprite extends Mesh<BufferGeometry,FixAxisSpriteMaterial> {
  constructor(material: FixAxisSpriteMaterial) {
    super(geometry, material)
  }
}


const glsl: any = {
  x: `
  uniform vec3 u_direction;
  uniform vec2 u_size;
  uniform vec2 u_scale;
  uniform vec2 u_center;
  varying vec2 v_uv;
  void main() { 
    v_uv=uv;
    vec4 center= modelViewMatrix * vec4(0.0,0.0,0.0,1.0);
    vec3 target=center.xyz/center.w;
    vec3 direction=target;
    float d=length(direction);
    vec3 x=normalize(normalMatrix*u_direction);
    vec3 y=normalize(cross(x,target));
    x=x.x/abs(x.x)* x;
    y=y.y/abs(y.y)* y;
    vec2 offset=(u_size+u_scale*d)*(uv-u_center);
    gl_Position = projectionMatrix * vec4( target+ x*offset.x+y*offset.y, 1.0 );
  }`,
  y: `
  uniform vec3 u_direction;
  uniform vec2 u_size;
  uniform vec2 u_scale;
  uniform vec2 u_center;
  varying vec2 v_uv;
  void main() { 
    v_uv=uv;
    vec4 center= modelViewMatrix * vec4(0.0,0.0,0.0,1.0);
    vec3 target=center.xyz/center.w;
    vec3 direction=target;
    float d=length(direction);
    vec3 y=normalize(normalMatrix*u_direction);
    vec3 x=normalize(cross(target,y));
    x=x.x/abs(x.x)* x;
    y=y.y/abs(y.y)* y;
    vec2 offset=(u_size+u_scale*d)*(uv-u_center);
    gl_Position = projectionMatrix * vec4( target+ x*offset.x+y*offset.y, 1.0 );
  }`,
  z: `
  uniform vec3 u_direction;
  uniform vec2 u_size;
  uniform vec2 u_scale;
  uniform vec2 u_center;
  varying vec2 v_uv;
  void main() { 
    v_uv=uv;
    vec4 center= modelViewMatrix * vec4(0.0,0.0,0.0,1.0);
    vec3 target=center.xyz/center.w;
    vec3 direction=target;
    vec3 normal=normalMatrix*u_direction;
    float d=length(direction);
    vec3 x=vec3(1.0,0.0,0.0);
    vec3 y=normalize(cross(normal,x));
    vec2 offset=(u_size+u_scale*d)*(uv-u_center);
    gl_Position = projectionMatrix * vec4( target+ x*offset.x+y*offset.y, 1.0 );
  }
  `,
  xyz: `
  uniform vec3 u_direction;
  uniform vec2 u_size;
  uniform vec2 u_scale;
  uniform vec2 u_center;
  varying vec2 v_uv;
  void main() { 
    v_uv=uv;
    vec4 center= modelMatrix * vec4(0.0,0.0,0.0,1.0);
    vec3 target=center.xyz/center.w;
    vec3 direction=target-cameraPosition;
    vec3 normal=u_direction;
    float d=length(direction);
    vec3 x=vec3(1.0,0.0,0.0);
    vec3 y=normalize(cross(normal,x));
    vec2 offset=(u_size+u_scale*d)*(uv-u_center);
    gl_Position = projectionMatrix*viewMatrix  * vec4( target+ x*offset.x+y*offset.y, 1.0 );
  }
    `,
  none: `
  uniform vec3 u_direction;
  uniform vec2 u_size;
  uniform vec2 u_scale;
  uniform vec2 u_center;
  varying vec2 v_uv;
  void main() { 
    v_uv=uv;
    vec4 center= modelViewMatrix * vec4(0.0,0.0,0.0,1.0);
    vec3 target=center.xyz/center.w;
    vec3 direction=target;
    float d=length(direction);
    vec3 x=vec3(1.0,0.0,0.0);
    vec3 y=vec3(0.0,1.0,0.0);
    vec2 offset=(u_size+u_scale*d)*(uv-u_center);
    gl_Position = projectionMatrix * vec4( target+ x*offset.x+y*offset.y, 1.0 );
  }
  `

}
